#coding:utf8
from django.views.generic import TemplateView,View
from django.http import HttpResponse,HttpResponseRedirect,JsonResponse,QueryDict
from django.utils.decorators import method_decorator
from django.db.models import Q

from pure_pagination import Paginator
from decorators import login_required
from datetime import date,datetime
from decimal import Decimal
import operator
import copy
import json

class JsonCustomEncoder(json.JSONEncoder):
    """
    自定义json类，json_dumps(json_data,cls=JsonCustomEncoder)
    """
    def default(self, field):
        if isinstance(field, datetime):
            return field.strftime('%Y-%m-%d %H:%M:%S')
        elif isinstance(field, date):
            return field.strftime('%Y-%m-%d')
        elif isinstance(field,Decimal):
            return str(field)
        else:
            return json.JSONEncoder.default(self, field)

class LoginRequiredBaseView(View):
    """
    登录判断继承类
    """
    @method_decorator(login_required)
    def dispatch(self, request, *args, **kwargs):
        return super(LoginRequiredBaseView, self).dispatch(request, *args, **kwargs)

class CreateBaseView(View):
    """
    modelform新增共用基类
    """
    form = None
    model = None

    def other_dict(self):
        return {}

    def before_save(self):
        pass

    def after_save(self,obj):
        pass

    def success_fuc(self,obj):
        return JsonResponse({'status':True,'msg':'success'})

    def error_fuc(self,errors):
        return JsonResponse({'status':False,'msg':'\n'.join(errors)})

    @method_decorator(login_required)
    def post(self, request):
        if self.before_save():
            return self.before_save()
        edit_id = self.request.POST.get('id', '')
        if edit_id:
            model_obj = self.model.objects.get(pk=edit_id)
            form = self.form(request.POST,instance=model_obj)
        else:
            form = self.form(request.POST)
        if form.is_valid():
            other_dict = self.other_dict()
            if other_dict:
                for k in other_dict:
                    form.instance.__dict__[k] = other_dict[k]
                obj = form.save()
                # 这种方法新增会丢多对多
                # obj = form.save(commit=False)
                # for k in other_dict:
                #     obj.__dict__[k] = other_dict[k]
                # obj.save()
            else:
                obj = form.save()
            self.after_save(obj)
            return self.success_fuc(obj)
        else:
            print unicode(form.errors)
            errors_list = []
            for key,value in form.errors.items():
                errors_list.append(key+value.get_json_data()[0]['message'])
            return self.error_fuc(errors_list)

class DeleteBaseView(View):
    """
    删除共用基类
    """
    model = None

    def before_delete(self,del_obj):
        pass

    def success_delete(self,obj):
        return JsonResponse({'status':True,'id':obj.get('id')})

    @method_decorator(login_required)
    def delete(self, request):
        DELETE = QueryDict(request.body)
        del_type = DELETE.get('del_type','0')
        if del_type == '0':         # 单条删除
            del_id = DELETE.get('id','')
            del_obj = self.model.objects.get(id=del_id)
            if self.before_delete(del_obj):
                return self.before_delete()
            bak_obj = copy.deepcopy(del_obj.__dict__)
            del_obj.delete()
        else:                       # 批量删除
            del_ids = DELETE.getlist('id[]',[])
            del_obj = self.model.objects.filter(id__in=del_ids)
            if self.before_delete(del_obj):
                return self.before_delete()
            del_obj.delete()
            bak_obj = {}
        return self.success_delete(bak_obj)

class TemplateBaseView(TemplateView,LoginRequiredBaseView):
    """
    列表页面展示共用基类
    """
    template_name = ''
    model = None
    pagenum = 50
    filter_dict = {}
    normal_user = ''
    default_order = '-id'    # 如果为列表则为多级排序 如：['-id','-create_at']
    related_names = []       # 关联查询的外键、一对一字段名称, 如果不加列表页会循环查询
    many_related_names = []  # 关联查询的多对多字段名称, 如果不加列表页会循环查询
    filter_status = False    # 是否已过滤

    def get_objects(self,context):
        '''
        获取model对象
        :return: 查询结果集
        '''
        dbname = self.request.GET.get('dbname','')
        if dbname:
            objs = self.model.objects.using(dbname).all()
        else:
            objs = self.model.objects.all()
        if self.related_names:
            objs = objs.select_related(*self.related_names)
        if self.many_related_names:
            objs = objs.prefetch_related(*self.many_related_names)
        objs = self.common_model_filter(objs,context)
        objs = self.other_filter(objs,context)
        objs = self.common_sort(objs,context)
        return objs

    def common_model_filter(self,pageobj,context):
        '''
        通用model过滤方法

        filter_dict = {ajax_name:field_name}
        ajax_name为前端name值，其中()为类型,[]为默认值,如果为多对多前缀为MTM-;
        field_name为后台model字段名，其中有|则为多条件or

        normal_user为普通用户过滤字段名(权限)
        '''
        if self.filter_dict:
            model_data = {}
            ajax_data = {}
            distinct = False
            for ajax_name,field_name in self.filter_dict.items():
                field_type = 'str'
                default_val = ''
                # MTM- 多对多筛选要加distinct
                if ajax_name.startswith('MTM-'):
                    distinct = True
                    ajax_name = ajax_name[4:]
                if '[' in ajax_name:
                    default_val = ajax_name.split('[')[1].split(']')[0]
                if '(' in ajax_name:
                    ajax_name,field_type = ajax_name.split('(')
                    field_type = field_type.split(')')[0]
                if self.request.method == 'GET':
                    field_val = self.request.GET.get(ajax_name)
                else:
                    field_val = self.request.POST.get(ajax_name)
                # search过滤空格
                if ajax_name == 'search' and field_val:
                    field_val = field_val.replace(' ','')
                if field_val:
                    if field_type == 'int':
                        field_val = int(field_val)
                    ajax_data[ajax_name] = field_val
                    if '|' in field_name:               # 多条件or查询
                        q_list = [Q(**{fn:field_val}) for fn in field_name.split('|')]
                        pageobj = pageobj.filter(reduce(operator.or_, q_list))
                        continue
                    if field_name:
                        model_data[field_name] = field_val
                elif default_val:                       # 默认值
                    if field_type == 'int':
                        default_val = int(default_val)
                    ajax_data[ajax_name] = default_val
                    if field_name:
                        model_data[field_name] = default_val
            # 记录是否筛选过
            _model_data = copy.deepcopy(ajax_data)
            if 'search' in _model_data:
                del _model_data['search']
            if _model_data:
                self.filter_status = True
            else:
                self.filter_status = False
            context.update({'filter_status':self.filter_status})
            pageobj = pageobj.filter(**model_data)
            if distinct:
                pageobj = pageobj.distinct()
            context.update(ajax_data)
        if self.normal_user and not self.request.user.is_superuser:
            pageobj = pageobj.filter(**{self.normal_user:self.request.user})
        return pageobj

    def common_sort(self,pageobj,context):
        '''
        通用排序方法(默认根据id倒序)

        table_sort = name-D/name-A
        name为需要排序的字段名
        D 倒序/A 顺序
        '''
        if self.request.method == 'GET':
            table_sort = self.request.GET.get('table_sort')
        else:
            table_sort = self.request.POST.get('table_sort')
        if table_sort:
            name,val = table_sort.split('-')
            context[name] = val
            context['table_sort'] = table_sort
            if val == 'D':
                pageobj = pageobj.order_by('-'+name)
            elif val == 'A':
                pageobj = pageobj.order_by(name)
        else:
            if type(self.default_order) == list:
                pageobj = pageobj.order_by(*self.default_order)
                for o in self.default_order:
                    if o.startswith('-'):
                        context[o[1:]] = 'D'
                    else:
                        context[o] = 'A'
            else:
                pageobj = pageobj.order_by(self.default_order)
                if self.default_order.startswith('-'):
                    context[self.default_order[1:]] = 'D'
                else:
                    context[self.default_order] = 'A'
        return pageobj

    def other_filter(self,pageobj,context):
        '''
        自定义筛选/更新context方法
        '''
        return pageobj

    def get(self,request):
        '''
        excel导出方法

        *heads 为,拼接的表头
        *keys 为,拼接的类属性： 有.的为外键关联属性，有()为model方法
        '''
        excel = self.request.GET.get("excel", False)
        if excel:
            import xlwt,os,time
            from djangodiv.settings import MEDIA_ROOT
            page = self.request.GET.get("page", 1)
            heads = self.request.GET.get('heads','').split(',')
            keys = self.request.GET.get('keys','').split(',')
            context = super(TemplateBaseView, self).get_context_data()
            pageobj = self.get_objects(context)
            pageobj = self.common_model_filter(pageobj,context)
            pageobj = self.other_filter(pageobj,context)
            pageobj = self.common_sort(pageobj,context)
            # excel导出部分
            row_counts = 65535
            obj_count = pageobj.count()
            if obj_count > 300000:
                return JsonResponse({'status':False,'msg':u'数据超过三十万，减少数量'})
            workbook = xlwt.Workbook(encoding='utf8')
            sheet_count = float(obj_count)/row_counts
            if sheet_count > 1:
                if obj_count % row_counts:
                    sheet_count = int(sheet_count) + 1
                else:
                    sheet_count = int(sheet_count)
                li = [c for c in range(1, sheet_count+1)]
            else:
                sheet_count = 1
                li = [sheet_count]
            for sheet in li:
                worksheet = workbook.add_sheet('sheet{}'.format(sheet))
                for i,head in enumerate(heads):
                    worksheet.write(0,i,head)
                for x,item in enumerate(pageobj[(sheet-1)*row_counts:sheet*row_counts]):
                    for y,key in enumerate(keys):
                        try:
                            if '.' in key:                  # 多层外键
                                k_list = key.split('.')
                                val = item
                                for k in k_list:
                                    if val:
                                        if k.endswith('()'):
                                            k = k[:-2]
                                            val = getattr(val,k).__call__()
                                        else:
                                            val = getattr(val,k)
                                    else:
                                        val=''
                            elif key.endswith('()'):        # model方法
                                key = key[:-2]
                                val = getattr(item,key).__call__()
                            else:
                                if type(item) == dict:
                                    val = item.get(key,'')
                                else:
                                    val = getattr(item,key)
                            if type(val) == type(datetime.datetime.now()):
                                val = datetime.datetime.strftime(val, '%Y-%m-%d %H:%M:%S')
                        except:
                            val = ''
                        worksheet.write(x+1,y,val)
            path = MEDIA_ROOT+'excel/'
            if not os.path.exists(path):
                os.makedirs(path)
            file_name = str(int(time.time()))+'.xls'
            workbook.save(path+file_name)
            url = '/medias/excel/'+file_name
            return JsonResponse({'status':True,'url':url})
        else:
            return super(TemplateBaseView, self).get(self,request)

    def get_page(self,context):
        '''
        django-pure-pagination分页方法
        '''
        if self.request.method == 'GET':
            page = self.request.GET.get("page", 1)
        else:
            page = self.request.POST.get("page", 1)
        try:
            page = int(page)
        except:
            page = 1
        pageobj = self.get_objects(context)
        p = Paginator(pageobj, self.pagenum, request=self.request)
        total = p.count
        if page > p.num_pages:
            page = p.num_pages
        objects = p.page(page)
        if not total:                   # 不写可能会报错
            objects.object_list = []
        context.update({
            'objects': objects,
            'total_page': p.num_pages,
            'total': total
        })
        return objects

    def get_context_data(self, **kwargs):
        context = super(TemplateBaseView, self).get_context_data()
        self.get_page(context)
        return context

class DetailBaseView(TemplateView,LoginRequiredBaseView):
    """
    详情页共用基类
    """
    template_name = ''
    model = None
    detail_form = None
    pk = 'id'

    def get_object(self):
        model_pk = self.request.GET.get(self.pk)
        obj = None
        if model_pk:
            objs = self.model.objects.filter(pk=model_pk)
            if objs.count():
                return objs[0]
        return obj

    def other_context(self,obj,context):
        pass

    def get_context_data(self, **kwargs):
        context = super(DetailBaseView, self).get_context_data()
        obj = self.get_object()
        if self.detail_form and obj:
            self.detail_form = self.detail_form(instance=obj)
        context.update({'obj': obj,'detail_form':self.detail_form})
        self.other_context(obj,context)
        return context